mod state_store;

use ane_system::entity::sys_operate_log;
use axum::{
    extract::FromRequestParts,
    http::Method,
    response::{IntoResponse, Response},
};
use casbin::CoreApi;
use common::utils;
use loginmanager::CurrentUser;
use sea_orm::{ActiveModelTrait, ActiveValue::NotSet, Set};
pub use state_store::StateStore;

use crate::{
    auth_manager::LoginUser,
    error::AppError,
    extract::{ClientIp, CollectBody, ExtractUserAgent},
    state::ShareState,
};

/// 使用的casbin做权限检查
/// - user_anonymous path method
/// - user_id path method
/// - role_id path method
pub async fn casbin_middleware(
    request: axum::extract::Request,
    next: axum::middleware::Next,
) -> Response {
    let path = request.uri().path();
    let method = request.method().as_str();
    let enforcer = request.extensions().get::<ShareState>().unwrap().enforcer();
    {
        let enforce = enforcer.lock().await;
        if !enforce.enforce(("user_anonymous", path, method)).unwrap() {
            if let Some(user) = request.extensions().get::<LoginUser>() {
                let mut per = enforce
                    .enforce((format!("user_{}", user.id), path, method))
                    .unwrap();
                for role in &user.roles {
                    if per {
                        break;
                    }
                    per = enforce
                        .enforce((format!("role_{}", role.id), path, method))
                        .unwrap();
                }
                if !per {
                    return AppError::Forbidden.into_response();
                }
            }
        }
    };
    next.run(request).await
}

/// 将[session](tower_sessions::Session)添加到response的extensions里面,
/// axum不会把request中的extensions转存到response中
pub async fn session_add_middleware(
    request: axum::extract::Request,
    next: axum::middleware::Next,
) -> axum::response::Response {
    let session = request
        .extensions()
        .get::<tower_sessions::Session>()
        .unwrap()
        .clone();
    let mut res = next.run(request).await;
    res.extensions_mut().insert(session);
    return res;
}

/// 记录操作记录
pub async fn operator_middleware(
    mut request: axum::extract::Request,
    next: axum::middleware::Next,
) -> axum::response::Response {
    let method = request.method();
    if !(method == Method::PUT || method == Method::POST || method == Method::DELETE) {
        return next.run(request).await;
    } else {
    }
    let method = method.to_string();
    let path = request.uri().path().to_owned();
    let query = request.uri().query().map(|s| s.to_owned());
    let body = CollectBody::default();
    request.extensions_mut().insert(body.clone());
    let mut parts = request.into_parts();
    let agent = ExtractUserAgent::from_request_parts(&mut parts.0, &{})
        .await
        .ok()
        .and_then(|f| f.0);
    let user = CurrentUser::<LoginUser>::from_request_parts(&mut parts.0, &{})
        .await
        .unwrap()
        .0
        .id;
    let ip = ClientIp::from_request_parts(&mut parts.0, &{})
        .await
        .ok()
        .and_then(|ip| ip.0)
        .map(|ip| ip.to_string());

    let request = axum::extract::Request::from_parts(parts.0, parts.1);
    let state = (request.extensions().get::<ShareState>().unwrap()).clone();
    let res = next.run(request).await;
    let active = sys_operate_log::ActiveModel {
        id: NotSet,
        result: Set(if res.status().is_success() { 0 } else { 1 }),
        request_method: Set(method),
        request_path: Set(path),
        request_query: Set(query),
        request_data: Set(body.into_body().await),
        user_ip: Set(ip),
        user_agent: Set(agent),
        operator_id: Set(user),
        created_at: Set(utils::current_datetime()),
    };
    if let Err(err) = active.insert(state.db()).await {
        tracing::error!("fail save operate log  {}", err.to_string());
    };
    return res;
}
